www.gusucode.com > Piwik 网站流量统计系统 v2.9.1PHP源码程序 > Piwik 网站流量统计系统 v2.9.1/piwik/piwik/plugins/ImageGraph/StaticGraph/GridGraph.php
<?php /** * Piwik - free/libre analytics platform * * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later * */ namespace Piwik\Plugins\ImageGraph\StaticGraph; use Piwik\Plugins\ImageGraph\StaticGraph; /** * */ abstract class GridGraph extends StaticGraph { const GRAPHIC_COLOR_KEY = 'GRAPHIC_COLOR'; const TRUNCATION_TEXT = '...'; const DEFAULT_TICK_ALPHA = 20; const DEFAULT_SERIE_WEIGHT = 0.5; const LEFT_GRID_MARGIN = 4; const BOTTOM_GRID_MARGIN = 10; const TOP_GRID_MARGIN_HORIZONTAL_GRAPH = 1; const RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH = 4; const OUTER_TICK_WIDTH = 5; const INNER_TICK_WIDTH = 0; const LABEL_SPACE_VERTICAL_GRAPH = 7; const HORIZONTAL_LEGEND_TOP_MARGIN = 5; const HORIZONTAL_LEGEND_LEFT_MARGIN = 10; const HORIZONTAL_LEGEND_BOTTOM_MARGIN = 10; const VERTICAL_LEGEND_TOP_MARGIN = 8; const VERTICAL_LEGEND_LEFT_MARGIN = 6; const VERTICAL_LEGEND_MAX_WIDTH_PCT = 0.70; const LEGEND_LINE_BULLET_WIDTH = 14; const LEGEND_BOX_BULLET_WIDTH = 5; const LEGEND_BULLET_RIGHT_PADDING = 5; const LEGEND_ITEM_HORIZONTAL_INTERSTICE = 6; const LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET = 4; const LEGEND_SHADOW_OPACITY = 25; const LEGEND_VERTICAL_SHADOW_PADDING = 3; const LEGEND_HORIZONTAL_SHADOW_PADDING = 2; const PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE = 5; protected function getDefaultColors() { return array( self::GRAPHIC_COLOR_KEY . '1' => '5170AE', self::GRAPHIC_COLOR_KEY . '2' => 'F29007', self::GRAPHIC_COLOR_KEY . '3' => 'CC3399', self::GRAPHIC_COLOR_KEY . '4' => '9933CC', self::GRAPHIC_COLOR_KEY . '5' => '80A033', self::GRAPHIC_COLOR_KEY . '6' => '246AD2' ); } protected function initGridChart( $displayVerticalGridLines, $bulletType, $horizontalGraph, $showTicks, $verticalLegend ) { $this->initpData(); $colorIndex = 1; foreach ($this->ordinateSeries as $column => $data) { $this->pData->setSerieWeight($column, self::DEFAULT_SERIE_WEIGHT); $graphicColor = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++]; $this->pData->setPalette($column, $graphicColor); } $this->initpImage(); // graph area coordinates $topLeftXValue = $this->getGridLeftMargin($horizontalGraph, $withLabel = true); $topLeftYValue = $this->getGridTopMargin($horizontalGraph, $verticalLegend); $bottomRightXValue = $this->width - $this->getGridRightMargin($horizontalGraph); $bottomRightYValue = $this->getGraphBottom($horizontalGraph); $this->drawBackground(); $this->pImage->setGraphArea( $topLeftXValue, $topLeftYValue, $bottomRightXValue, $bottomRightYValue ); // determine how many labels need to be skipped $skippedLabels = 0; if (!$horizontalGraph) { list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries); $graphWidth = $bottomRightXValue - $topLeftXValue; $maxNumOfLabels = floor($graphWidth / ($abscissaMaxWidth + self::LABEL_SPACE_VERTICAL_GRAPH)); $abscissaSeriesCount = count($this->abscissaSeries); if ($maxNumOfLabels < $abscissaSeriesCount) { for ($candidateSkippedLabels = 1; $candidateSkippedLabels < $abscissaSeriesCount; $candidateSkippedLabels++) { $numberOfSegments = $abscissaSeriesCount / ($candidateSkippedLabels + 1); $numberOfCompleteSegments = floor($numberOfSegments); $numberOfLabels = $numberOfCompleteSegments; if ($numberOfSegments > $numberOfCompleteSegments) { $numberOfLabels++; } if ($numberOfLabels <= $maxNumOfLabels) { $skippedLabels = $candidateSkippedLabels; break; } } } if ($this->forceSkippedLabels && $skippedLabels && $skippedLabels < $this->forceSkippedLabels && $abscissaSeriesCount > $this->forceSkippedLabels + 1 ) { $skippedLabels = $this->forceSkippedLabels; } } $ordinateAxisLength = $horizontalGraph ? $bottomRightXValue - $topLeftXValue : $this->getGraphHeight($horizontalGraph, $verticalLegend); $maxOrdinateValue = 0; foreach ($this->ordinateSeries as $column => $data) { $currentMax = $this->pData->getMax($column); if ($currentMax > $maxOrdinateValue) { $maxOrdinateValue = $currentMax; } } // rounding top scale value to the next multiple of 10 if ($maxOrdinateValue > 10) { $modTen = $maxOrdinateValue % 10; if ($modTen) $maxOrdinateValue += 10 - $modTen; } $gridColor = $this->gridColor; $this->pImage->drawScale( array( 'Mode' => SCALE_MODE_MANUAL, 'GridTicks' => 0, 'LabelSkip' => $skippedLabels, 'DrawXLines' => $displayVerticalGridLines, 'Factors' => array(ceil($maxOrdinateValue / 2)), 'MinDivHeight' => $ordinateAxisLength / 2, 'AxisAlpha' => 0, 'SkippedAxisAlpha' => 0, 'TickAlpha' => $showTicks ? self::DEFAULT_TICK_ALPHA : 0, 'InnerTickWidth' => self::INNER_TICK_WIDTH, 'OuterTickWidth' => self::OUTER_TICK_WIDTH, 'GridR' => $gridColor['R'], 'GridG' => $gridColor['G'], 'GridB' => $gridColor['B'], 'GridAlpha' => 100, 'ManualScale' => array( 0 => array( 'Min' => 0, 'Max' => $maxOrdinateValue ) ), 'Pos' => $horizontalGraph ? SCALE_POS_TOPBOTTOM : SCALE_POS_LEFTRIGHT, ) ); if ($this->showLegend) { switch ($bulletType) { case LEGEND_FAMILY_LINE: $bulletWidth = self::LEGEND_LINE_BULLET_WIDTH; // measured using a picture editing software $iconOffsetAboveLabelSymmetryAxis = -2; break; case LEGEND_FAMILY_BOX: $bulletWidth = self::LEGEND_BOX_BULLET_WIDTH; // measured using a picture editing software $iconOffsetAboveLabelSymmetryAxis = 3; break; } // pChart requires two coordinates to draw the legend $legendTopLeftXValue & $legendTopLeftYValue // $legendTopLeftXValue = legend's left padding $legendTopLeftXValue = $topLeftXValue + ($verticalLegend ? self::VERTICAL_LEGEND_LEFT_MARGIN : self::HORIZONTAL_LEGEND_LEFT_MARGIN); // $legendTopLeftYValue = y coordinate of the top edge of the legend's icons // Caution : // - pChart will silently add some value (see $paddingAddedByPChart) to $legendTopLeftYValue depending on multiple criterias // - pChart will not take into account the size of the text. Setting $legendTopLeftYValue = 0 will crop the legend's labels // The following section of code determines the value of $legendTopLeftYValue while taking into account the following paremeters : // - whether legend items have icons // - whether icons are bigger than the legend's labels // - how much colored shadow padding is required list($maxLogoWidth, $maxLogoHeight) = self::getMaxLogoSize(array_values($this->ordinateLogos)); if ($maxLogoHeight >= $this->legendFontSize) { $heightOfTextAboveBulletTop = 0; $paddingCreatedByLogo = $maxLogoHeight - $this->legendFontSize; $effectiveShadowPadding = $paddingCreatedByLogo < self::LEGEND_VERTICAL_SHADOW_PADDING * 2 ? self::LEGEND_VERTICAL_SHADOW_PADDING - ($paddingCreatedByLogo / 2) : 0; } else { if ($maxLogoHeight) { // measured using a picture editing software $iconOffsetAboveLabelSymmetryAxis = 5; } $heightOfTextAboveBulletTop = $this->legendFontSize / 2 - $iconOffsetAboveLabelSymmetryAxis; $effectiveShadowPadding = self::LEGEND_VERTICAL_SHADOW_PADDING; } $effectiveLegendItemVerticalInterstice = $this->legendFontSize + self::LEGEND_ITEM_VERTICAL_INTERSTICE_OFFSET; $effectiveLegendItemHorizontalInterstice = self::LEGEND_ITEM_HORIZONTAL_INTERSTICE + self::LEGEND_HORIZONTAL_SHADOW_PADDING; $legendTopMargin = $verticalLegend ? self::VERTICAL_LEGEND_TOP_MARGIN : self::HORIZONTAL_LEGEND_TOP_MARGIN; $requiredPaddingAboveItemBullet = $legendTopMargin + $heightOfTextAboveBulletTop + $effectiveShadowPadding; $paddingAddedByPChart = 0; if ($verticalLegend) { if ($maxLogoHeight) { // see line 1691 of pDraw.class.php if ($maxLogoHeight < $effectiveLegendItemVerticalInterstice) { $paddingAddedByPChart = ($effectiveLegendItemVerticalInterstice / 2) - ($maxLogoHeight / 2); } } else { // see line 1711 of pDraw.class.php ($Y+$IconAreaHeight/2) $paddingAddedByPChart = $effectiveLegendItemVerticalInterstice / 2; } } $legendTopLeftYValue = $paddingAddedByPChart < $requiredPaddingAboveItemBullet ? $requiredPaddingAboveItemBullet - $paddingAddedByPChart : 0; // add colored background to each legend item if (count($this->ordinateLabels) > 1) { $currentPosition = $verticalLegend ? $legendTopMargin : $legendTopLeftXValue; $colorIndex = 1; foreach ($this->ordinateLabels as $metricCode => &$label) { $color = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++]; $paddedBulletWidth = $bulletWidth; if (isset($this->ordinateLogos[$metricCode])) { $paddedBulletWidth = $maxLogoWidth; } $paddedBulletWidth += self::LEGEND_BULLET_RIGHT_PADDING; // truncate labels if required if ($verticalLegend) { $label = $this->truncateLabel($label, ($this->width * self::VERTICAL_LEGEND_MAX_WIDTH_PCT) - $legendTopLeftXValue - $paddedBulletWidth, $this->legendFontSize); $this->pData->setSerieDescription($metricCode, $label); } $rectangleTopLeftXValue = ($verticalLegend ? $legendTopLeftXValue : $currentPosition) + $paddedBulletWidth - self::LEGEND_HORIZONTAL_SHADOW_PADDING; $rectangleTopLeftYValue = $verticalLegend ? $currentPosition : $legendTopMargin; list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $this->legendFontSize); $legendItemWidth = $paddedBulletWidth + $labelWidth + $effectiveLegendItemHorizontalInterstice; $rectangleBottomRightXValue = $rectangleTopLeftXValue + $labelWidth + (self::LEGEND_HORIZONTAL_SHADOW_PADDING * 2); $legendItemHeight = max($maxLogoHeight, $this->legendFontSize) + ($effectiveShadowPadding * 2); $rectangleBottomRightYValue = $rectangleTopLeftYValue + $legendItemHeight; $this->pImage->drawFilledRectangle( $rectangleTopLeftXValue, $rectangleTopLeftYValue, $rectangleBottomRightXValue, $rectangleBottomRightYValue, array( 'Alpha' => self::LEGEND_SHADOW_OPACITY, 'R' => $color['R'], 'G' => $color['G'], 'B' => $color['B'], ) ); if ($verticalLegend) { $currentPositionIncrement = max($maxLogoHeight, $effectiveLegendItemVerticalInterstice, $this->legendFontSize) + self::PCHART_HARD_CODED_VERTICAL_LEGEND_INTERSTICE; } else { $currentPositionIncrement = $legendItemWidth; } $currentPosition += $currentPositionIncrement; } } // draw legend $legendColor = $this->textColor; $this->pImage->drawLegend( $legendTopLeftXValue, $legendTopLeftYValue, array( 'Style' => LEGEND_NOBORDER, 'FontSize' => $this->legendFontSize, 'BoxWidth' => $bulletWidth, 'XSpacing' => $effectiveLegendItemHorizontalInterstice, // not effective when vertical 'Mode' => $verticalLegend ? LEGEND_VERTICAL : LEGEND_HORIZONTAL, 'BoxHeight' => $verticalLegend ? $effectiveLegendItemVerticalInterstice : null, 'Family' => $bulletType, 'FontR' => $legendColor['R'], 'FontG' => $legendColor['G'], 'FontB' => $legendColor['B'], ) ); } } protected static function getMaxLogoSize($logoPaths) { $maxLogoWidth = 0; $maxLogoHeight = 0; foreach ($logoPaths as $logoPath) { list($logoWidth, $logoHeight) = self::getLogoSize($logoPath); if ($logoWidth > $maxLogoWidth) { $maxLogoWidth = $logoWidth; } if ($logoHeight > $maxLogoHeight) { $maxLogoHeight = $logoHeight; } } return array($maxLogoWidth, $maxLogoHeight); } protected static function getLogoSize($logoPath) { $pathInfo = getimagesize($logoPath); return array($pathInfo[0], $pathInfo[1]); } protected function getGridLeftMargin($horizontalGraph, $withLabel) { $gridLeftMargin = self::LEFT_GRID_MARGIN + self::OUTER_TICK_WIDTH; if ($withLabel) { list($maxTextWidth, $maxTextHeight) = $this->getMaximumTextWidthHeight($horizontalGraph ? $this->abscissaSeries : $this->ordinateSeries); $gridLeftMargin += $maxTextWidth; } return $gridLeftMargin; } protected function getGridTopMargin($horizontalGraph, $verticalLegend) { list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries); if ($horizontalGraph) { $topMargin = $ordinateMaxHeight + self::TOP_GRID_MARGIN_HORIZONTAL_GRAPH + self::OUTER_TICK_WIDTH; } else { $topMargin = $ordinateMaxHeight / 2; } if ($this->showLegend && !$verticalLegend) { $topMargin += $this->getHorizontalLegendHeight(); } return $topMargin; } private function getHorizontalLegendHeight() { list($maxMetricLegendWidth, $maxMetricLegendHeight) = $this->getMaximumTextWidthHeight(array_values($this->ordinateLabels), $this->legendFontSize); return $maxMetricLegendHeight + self::HORIZONTAL_LEGEND_BOTTOM_MARGIN + self::HORIZONTAL_LEGEND_TOP_MARGIN; } protected function getGraphHeight($horizontalGraph, $verticalLegend) { return $this->getGraphBottom($horizontalGraph) - $this->getGridTopMargin($horizontalGraph, $verticalLegend); } private function getGridBottomMargin($horizontalGraph) { $gridBottomMargin = self::BOTTOM_GRID_MARGIN; if (!$horizontalGraph) { list($abscissaMaxWidth, $abscissaMaxHeight) = $this->getMaximumTextWidthHeight($this->abscissaSeries); $gridBottomMargin += $abscissaMaxHeight; } return $gridBottomMargin; } protected function getGridRightMargin($horizontalGraph) { if ($horizontalGraph) { // in horizontal graphs, metric values are displayed on the far right of the bar list($ordinateMaxWidth, $ordinateMaxHeight) = $this->getMaximumTextWidthHeight($this->ordinateSeries); return self::RIGHT_GRID_MARGIN_HORIZONTAL_GRAPH + $ordinateMaxWidth; } else { return 0; } } protected function getGraphBottom($horizontalGraph) { return $this->height - $this->getGridBottomMargin($horizontalGraph); } protected function truncateLabel($label, $labelWidthLimit, $fontSize = false) { list($truncationTextWidth, $truncationTextHeight) = $this->getTextWidthHeight(self::TRUNCATION_TEXT, $fontSize); list($labelWidth, $labelHeight) = $this->getTextWidthHeight($label, $fontSize); if ($labelWidth > $labelWidthLimit) { $averageCharWidth = $labelWidth / strlen($label); $charsToKeep = floor(($labelWidthLimit - $truncationTextWidth) / $averageCharWidth); $label = substr($label, 0, $charsToKeep) . self::TRUNCATION_TEXT; } return $label; } // display min & max values // can not currently be used because pChart's label design is not flexible enough // e.g: it is not possible to remove the box border & the square icon // it would require modifying pChart code base which we try to avoid // see https://github.com/piwik/piwik/issues/3396 // protected function displayMinMaxValues() // { // if ($displayMinMax) // { // // when plotting multiple metrics, display min & max on both series // // to fix: in vertical bars, labels are hidden when multiple metrics are plotted, hence the restriction on count($this->ordinateSeries) == 1 // if ($this->multipleMetrics && count($this->ordinateSeries) == 1) // { // $colorIndex = 1; // foreach($this->ordinateSeries as $column => $data) // { // $color = $this->colors[self::GRAPHIC_COLOR_KEY . $colorIndex++]; // // $this->pImage->writeLabel( // $column, // self::locateMinMaxValue($data), // $Format = array( // 'NoTitle' => true, // 'DrawPoint' => false, // 'DrawSerieColor' => true, // 'TitleMode' => LABEL_TITLE_NOBACKGROUND, // 'GradientStartR' => $color['R'], // 'GradientStartG' => $color['G'], // 'GradientStartB' => $color['B'], // 'GradientEndR' => 255, // 'GradientEndG' => 255, // 'GradientEndB' => 255, // 'BoxWidth' => 0, // 'VerticalMargin' => 9, // 'HorizontalMargin' => 7, // ) // ); // } // } // else // { // // display only one min & max label // } // } // } // protected static function locateMinMaxValue($data) // { // $firstValue = $data[0]; // $minValue = $firstValue; // $minValueIndex = 0; // $maxValue = $firstValue; // $maxValueIndex = 0; // foreach($data as $index => $value) // { // if ($value > $maxValue) // { // $maxValue = $value; // $maxValueIndex = $index; // } // // if ($value < $minValue) // { // $minValue = $value; // $minValueIndex = $index; // } // } // // return array($minValueIndex, $maxValueIndex); // } }